﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Device.Location;
using System.Globalization;

namespace WindowsPhoneServer
{
    public class GeoConvertor
    {
        private const string RMC_HEADER = "$GPRMC";
        private const string GGA_HEADER = "$GPGGA";
        private const string STATUS_A = "A";
        private const string STATUS_V = "V";
        private const string STATUS_GPS = "1";
        private const string STATUS_0 = "0";
        private const string LATITUDE_N = "N";
        private const string LATITUDE_S = "S";
        private const string LONGTUDE_E = "E";
        private const string LONGTUDE_W = "W";
        private const int SATELLITE_COUNT = 5;
        private const string ALTITUDE_M = "M";
        private static CultureInfo culture = new CultureInfo("en-US");


        static void DegreeToString(double value, out int d, out int m, out int mm)
        {
            value = Math.Abs(value);
            d = (int)value;
            m = (int)((value - d) * 60);
            mm = (int)(((value - d) * 60 - m) * 1000000);
        }

        static double DegreeFromString(string str)
        {
            double value;
            string mm = str.Substring(2);
            string d = str.Substring(0, 2);
            value = Convert.ToDouble(mm) / 60;
            value += Convert.ToDouble(d);
            return value;
        }

        public static byte crc8(string str)
        {
            byte crc = 0;
            str = str.Substring(1, str.Length - 1);
            foreach (char ch in str)
                crc ^=  Convert.ToByte(ch);
            return crc;
        }
        
        public static string ToRMC(GeoCoordinate geoCoordinate)
        {
            string rmc = "";
            string[] arrRMC = new string[13];

            arrRMC[0] = RMC_HEADER;

            DateTime dt = DateTime.Now.ToUniversalTime();
            arrRMC[1] = string.Format("{0:00}{1:00}{2:00}.{3:00}", dt.Hour, dt.Minute, dt.Second, 0/*dt.Millisecond*/);     // время

            arrRMC[2] = string.Format("{0}", (geoCoordinate.IsUnknown ? STATUS_V : STATUS_A));     // статус

            int latD, latM, latMM;
            DegreeToString(geoCoordinate.Latitude, out latD, out latM, out latMM);                              //широта
            arrRMC[3] = string.Format("{0:00}{1:00}.{2:000}", latD, latM, latMM);                                     //

            arrRMC[4] = string.Format("{0}", (0 < geoCoordinate.Latitude ? LATITUDE_N : LATITUDE_S));   //тип широты(северная/южная)

            int lonD, lonM, lonMM;
            DegreeToString(geoCoordinate.Longitude, out lonD, out lonM, out lonMM);                             // долгота
            arrRMC[5] = string.Format("{0:000}{1:00}.{2:000}", lonD, lonM, lonMM);                                     //

            arrRMC[6] = string.Format("{0}", (0 < geoCoordinate.Longitude ? LONGTUDE_E : LONGTUDE_W));  // тип долготы(восточная/западная)

            arrRMC[7] = string.Format(culture, "{0:000.0}", (double.IsNaN(geoCoordinate.Speed) ? 0 : geoCoordinate.Speed * 1.9438));           // скорость

            arrRMC[8] = string.Format(culture, "{0:000.0}", double.IsNaN(geoCoordinate.Course) ? 0 : geoCoordinate.Course);           // курс

            arrRMC[9] = string.Format("{0:00}{1:00}{2:00}", dt.Day, dt.Month, dt.Year - dt.Year / 100 * 100);     // дата

            arrRMC[10] = "";     // магнитное склонение
            arrRMC[11] = "";     // направление магнитного склонения
            arrRMC[12] = "A";     // режим

            foreach (string str in arrRMC)
                rmc += string.Format("{0},", str);
            rmc = rmc.Remove(rmc.Length - 1);

            rmc += string.Format("*{0:X}", crc8(rmc));
            return rmc;
        }

        public static GeoCoordinate FromRMC(string rmc)
        {
            GeoCoordinate geoCoordinate = null;
            string[] arrRMC = rmc.Split(',');

            if (RMC_HEADER == arrRMC[0])
            {
                geoCoordinate = new GeoCoordinate();
                //geoCoordinate.Altitude = Convert.ToDouble();
                geoCoordinate.Course = Convert.ToDouble(arrRMC[8]);
                //geoCoordinate.HorizontalAccuracy;
                geoCoordinate.Latitude = (LATITUDE_S != arrRMC[4]? DegreeFromString(arrRMC[3]): -DegreeFromString(arrRMC[3]));
                geoCoordinate.Longitude = (LONGTUDE_W != arrRMC[6]? DegreeFromString(arrRMC[5]): -DegreeFromString(arrRMC[5]));
                geoCoordinate.Speed = Convert.ToDouble(arrRMC[7]) / 1.9438;
            }
            return geoCoordinate;
        }

        public static string ToGGA(GeoCoordinate geoCoordinate)
        {
            string str = "";
            string[] arrStr = new string[15];

            arrStr[0] = GGA_HEADER;

            DateTime dt = DateTime.Now.ToUniversalTime();
            arrStr[1] = string.Format("{0:00}{1:00}{2:00}.{3:00}", dt.Hour, dt.Minute, dt.Second, 0);     // время

            int latD, latM, latMM;
            DegreeToString(geoCoordinate.Latitude, out latD, out latM, out latMM);                              //широта
            arrStr[2] = string.Format("{0:00}{1:00}.{2:000}", latD, latM, latMM);                                     //

            arrStr[3] = string.Format("{0}", (0 < geoCoordinate.Latitude ? LATITUDE_N : LATITUDE_S));   //тип широты(северная/южная)

            int lonD, lonM, lonMM;
            DegreeToString(geoCoordinate.Longitude, out lonD, out lonM, out lonMM);                             // долгота
            arrStr[4] = string.Format("{0:000}{1:00}.{2:000}", lonD, lonM, lonMM);                                     //

            arrStr[5] = string.Format("{0}", (0 < geoCoordinate.Longitude ? LONGTUDE_E : LONGTUDE_W));  // тип долготы(восточная/западная)

            arrStr[6] = string.Format("{0}", (geoCoordinate.IsUnknown ? STATUS_0 : STATUS_GPS));     // Индикатор качества GPS сигнала:

            arrStr[7] = string.Format("{0:00}", 5);                                   // количество используемых спутников

            arrStr[8] = string.Format(culture, "{0:0.0}", 0.5/*geoCoordinate.HorizontalAccuracy*/);           // Фактор Ухудшения Точности Плановых Координат (HDOP)

            arrStr[9] = string.Format(culture, "{0:0.0}", double.IsNaN(geoCoordinate.Altitude) ? 0 : geoCoordinate.Altitude);     // высота

            arrStr[10] = string.Format("{0}", ALTITUDE_M);                                  // Единица измерения высоты расположения антенны, метры.
            
            arrStr[11] = "";                                        // Геоидальное различие 
            arrStr[12] = "";//string.Format("{0}", ALTITUDE_M);          // Единица измерения различия,
            arrStr[13] = "";                                        // Возраст Дифференциальных данных GPS 
            arrStr[14] = "";                                        // Индификатор станции,

            foreach (string s in arrStr)
                str += string.Format("{0},", s);
            str = str.Remove(str.Length - 1);

            str += string.Format("*{0:X}", crc8(str));
            //str += (char)(0x0D);
            //str += (char)(0x0A);
            return str;
        }

        public static GeoCoordinate FromGGA(string str)
        {
            GeoCoordinate geoCoordinate = null;
            string[] arrStr = str.Split(',');

            if (GGA_HEADER == arrStr[0])
            {
                geoCoordinate = new GeoCoordinate();
                geoCoordinate.Altitude = (ALTITUDE_M == arrStr[10] ? Convert.ToDouble(arrStr[9]) : Convert.ToDouble(arrStr[9]) * 0.3048);
                //geoCoordinate.Course = Convert.ToDouble(arrStr[8]);
                geoCoordinate.HorizontalAccuracy = Convert.ToDouble(arrStr[8]);
                geoCoordinate.Latitude = (LATITUDE_S != arrStr[3] ? DegreeFromString(arrStr[2]) : -DegreeFromString(arrStr[2]));
                geoCoordinate.Longitude = (LONGTUDE_W != arrStr[5] ? DegreeFromString(arrStr[4]) : -DegreeFromString(arrStr[4]));
                //geoCoordinate.Speed = Convert.ToDouble(arrStr[7]) / 1.9438;
                //geoCoordinate.VerticalAccuracy;
            }
            return geoCoordinate;
        }
    }
}
